/**
 *   Copyright (c) fanguangping. All rights reserved.
 *   The use and distribution terms for this software are covered by the
 *   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
 *   which can be found in the file epl-v10.html at the root of this distribution.
 *   By using this software in any fashion, you are agreeing to be bound by
 * 	 the terms of this license.
 *   You must not remove this notice, or any other, from this software.
 **/
package org.jfunny.core.util;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

/**
 * 
 * @author fanguangping
 *
 */
public class ReflectionUtil {
	
	/**
	 * 得到带构造的类的实例
	 * */
	public static Object newInstance(Class clazz, Object[] args) throws Exception {     
	    Class[] argsClass = new Class[args.length];                                   
	    for (int i = 0; i < args.length; i++) {                                
	        argsClass[i] = args[i].getClass();                                        
	    }	  
	    Constructor cons = clazz.getConstructor(argsClass);	    
	    return cons.newInstance(args);                                                 
	}
	
	/**
	 * 给一个接口，返回这个接口的所有实现类
	 * @param clazz
	 * @param packageName
	 * @return
	 */
	public static List<Class> findClassesByPackageName(Class clazz, String packageName) {
		List<Class> result = new ArrayList<Class>();
		
		if (clazz.isInterface()) {
			try {
                List<Class> allClass = getClassesByPackageName(packageName);
                for (int i = 0; i < allClass.size(); i++) {
                    /**
                     * 判定此 Class 对象所表示的类或接口与指定的 Class 参数cls所表示的类或接口是否相同，
                     * 或是否是其超类或(超)接口，如果是则返回 true，否则返回 false。
                     */
                    if (clazz.isAssignableFrom(allClass.get(i))) {
                        if (!clazz.equals(allClass.get(i))) {
                        	result.add(allClass.get(i));
                        }
                    }
                }
            } catch (ClassNotFoundException e) {
            	e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
		}
		return result;
	}
	

	/**
	 * 给一个接口，返回这个接口在当前包的所有实现类
	 * @param c
	 * @return
	 */
	public static List<Class> findClassesByInterface(Class c) {  
		return findClassesByPackageName(c, c.getPackage().getName());
    }  
	
	/**
	 * 从一个包中查找出所有类,在jar包中不能查找
	 * @param packageName
	 * @return
	 * @throws IOException
	 * @throws ClassNotFoundException
	 */
    private static List<Class> getClassesByPackageName(String packageName) throws IOException, ClassNotFoundException {  
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        String path = packageName.replace('.', '/');
        Enumeration<URL> resources = classLoader.getResources(path);
        List<File> dirs = new ArrayList<File>();
        while (resources.hasMoreElements()) {
            URL resource = resources.nextElement();
            dirs.add(new File(resource.getFile()));
        }
        ArrayList<Class> classes = new ArrayList<Class>();
        for (File directory : dirs) {
            classes.addAll(findClasses(directory, packageName));
        }  
        return classes;
    }

    private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException {  
        List<Class> classes = new ArrayList<Class>();  
        if (!directory.exists()) {  
            return classes;  
        }
        
        File[] files = directory.listFiles();  
        for (File file : files) {  
            if (file.isDirectory()) {  
                //递归查找文件夹【即对应的包】下面的所有文件
                assert !file.getName().contains(".");  
                classes.addAll(findClasses(file, packageName + '.' + file.getName()));  
            } else if (file.getName().endsWith(".class")) {  
                classes.add(Class.forName(packageName + "." + file.getName().substring(0,file.getName().length() - 6)));  
            }  
        }  
        return classes;  
    }
}
